Skip to content

move BeanCacheObservers into the registry#2556

Merged
predic8 merged 4 commits into
masterfrom
registry-aware
Jan 7, 2026
Merged

move BeanCacheObservers into the registry#2556
predic8 merged 4 commits into
masterfrom
registry-aware

Conversation

@rrayst

@rrayst rrayst commented Jan 7, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Dynamic observer registration for bean events and ability to detect produced bean types.
  • Improvements

    • More explicit registry startup and bean registration flow; earlier lifecycle startup sequencing.
    • Stronger lifecycle support: registry-aware wiring and PostConstruct/PreDestroy handling.
  • Bug Fixes

    • Clearer parsing error for missing/blank class in YAML bean specs.
  • Tests

    • Updated tests to reflect new registry initialization and parsing behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 7, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@rrayst has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 44 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9fa9828 and d0098b0.

📒 Files selected for processing (4)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • annot/src/main/java/com/predic8/membrane/annot/yaml/GenericYamlParser.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java
📝 Walkthrough

Walkthrough

Refactors bean-registry and YAML parsing: BeanCacheObserver API updated, BeanContainer now tracks produced class and adds produces(), BeanRegistryImplementation moves to runtime observer registration and a single-arg constructor, GenericYamlParser adds decideClazz() and wires BeanRegistryAware before PostConstruct. Multiple call sites updated to explicit register/start flows.

Changes

Cohort / File(s) Summary
Bean Registry Core APIs
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanCacheObserver.java, annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java, annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
BeanCacheObserver: handleBeanEvent(..., Object bean, Object oldBean)handleBeanEvent(..., Object newBean, Object oldBean) and isActivatable(BeanDefinition)isActivatable(BeanContainer). BeanContainer: new clazz field, updated constructors, and public <T> boolean produces(Class<T>). BeanRegistryImplementation: constructor reduced to (Grammar), observer list added, dynamic observer registration, handleBeanContainerChange + observer helper methods, register(name, bean) and start() usage introduced.
YAML Parsing
annot/src/main/java/com/predic8/membrane/annot/yaml/GenericYamlParser.java
Added public static Class<?> decideClazz(String kind, Grammar grammar, JsonNode node) to determine bean class without instantiation; readMembraneObject now uses it. Post-construction handling updated to inject BeanRegistry into BeanRegistryAware beans before invoking @PostConstruct.
Integration & Call Sites
core/src/main/java/.../RouterCLI.java, DefaultMainComponents.java, KubernetesWatcher.java, DefaultRouter.java, TestRouter.java, core/.../BalancerHealthMonitor.java
Updated to new BeanRegistryImplementation constructor; explicit registry.register("router", router) calls added. DefaultRouter isActivatable now accepts BeanContainer and uses bc.produces(Proxy.class). BalancerHealthMonitor implements BeanRegistryAware and gains lifecycle annotations.
Tests & Test Utilities
annot/src/test/java/.../YAMLBeanParsingTest.java, annot/src/test/java/.../beanregistry/BeanRegistryImplementationTest.java, annot/src/test/java/.../util/YamlParser.java
Tests updated to new constructor/registration flow and to assert missing-class errors at parse time. Test utilities simplified: removed latch/TestRouter synchronization, call impl.start() after parsing, and use single-arg BeanRegistryImplementation.

Sequence Diagram(s)

sequenceDiagram
    participant Parser as GenericYamlParser
    participant Registry as BeanRegistryImplementation
    participant Container as BeanContainer
    participant Observer as BeanCacheObserver
    participant Bean

    Note over Parser,Registry: YAML parsing and bean creation flow
    Parser->>Parser: decideClazz(kind, grammar, node)
    Parser->>Bean: createAndPopulateNode(clazz)
    Parser->>Registry: return bean (BeanRegistryAware wired)
    Registry->>Container: new BeanContainer(definition, bean)
    Container->>Container: set clazz (bean.getClass() / decideClazz)
    Registry->>Registry: register(name, container)
    alt bean produces BeanCacheObserver
        Registry->>Registry: add to observers list
    end
    Registry->>Observer: handleBeanEvent(BeanDefinitionChanged, newBean, oldBean)
    Observer->>Registry: isActivatable(BeanContainer)
    Note over Registry,Observer: observer-driven activation/async init notifications
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • predic8
  • christiangoerdes

Poem

🐰 I sniffed the YAML, found each class with care,
I hop and I register beans everywhere,
Observers now gather, awake in a row,
Constructors trimmed down so the startup can flow,
Hooray — types known, and the registry's fair!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'move BeanCacheObservers into the registry' directly and accurately reflects the main architectural change: refactoring how BeanCacheObservers are managed within the registry system.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rrayst

rrayst commented Jan 7, 2026

Copy link
Copy Markdown
Member Author

/ok-to-test

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
annot/src/test/java/com/predic8/membrane/annot/util/YamlParser.java (1)

43-43: Remove unused CountDownLatch.

The cdl variable is created but never used, as the synchronization logic has been removed in this refactoring. Additionally, the import on line 23 and InterruptedException in the constructor's throws clause (line 40) are now unnecessary.

🧹 Proposed cleanup

Remove the unused variable:

-        CountDownLatch cdl = new CountDownLatch(1);
-

Remove the unused import:

-import java.util.concurrent.CountDownLatch;

Remove the unused exception from the throws clause:

-    public YamlParser(String resourceName) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, InterruptedException {
+    public YamlParser(String resourceName) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
🤖 Fix all issues with AI agents
In
@annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:
- Around line 260-266: The method handleAsynchronousInitializationResult(boolean
empty) currently ignores its parameter and recalculates the emptiness via
uidsToActivate.isEmpty(); change the implementation to use the provided
parameter by replacing
observer.handleAsynchronousInitializationResult(uidsToActivate.isEmpty()) with
observer.handleAsynchronousInitializationResult(empty) inside the
synchronized(observers) loop so callers' intent is respected and the parameter
is no longer unused.

In @core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java:
- Line 38: Remove the unused import jakarta.annotation.PostConstruct from
DefaultRouter.java; delete the import statement (the line containing
PostConstruct) and run an import organizer/formatter to ensure no other unused
imports remain and compilation stays clean.
🧹 Nitpick comments (4)
annot/src/test/java/com/predic8/membrane/annot/YAMLBeanParsingTest.java (1)

103-113: Consider renaming test method to reflect parsing-time validation.

The test now verifies that the exception is thrown during parse(), not during resolve(). The method name missingClassFailsFastOnResolve is now misleading.

📝 Suggested rename
     @Test
-    void missingClassFailsFastOnResolve() {
+    void missingClassFailsFastOnParse() {
         var ex = assertThrows(RuntimeException.class, () -> {
             BeanRegistry r = parse("""
core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (1)

178-184: Consider extracting duplicate registry initialization logic.

The lazy initialization pattern in getRegistry() duplicates the logic in init(). While this provides safe access before init() is called, consider extracting to a private helper method to avoid drift.

♻️ Suggested refactor
+    private BeanRegistry ensureRegistry() {
+        if (registry == null) {
+            registry = new BeanRegistryImplementation(null);
+            registry.register("router", router);
+        }
+        return registry;
+    }
+
     public void init() {
         log.debug("Initializing.");
 
-        if (registry == null) {
-            registry = new BeanRegistryImplementation(null);
-            registry.register("router", router);
-        }
+        ensureRegistry();
 
         registry.registerIfAbsent(HttpClientConfiguration.class, HttpClientConfiguration::new);
         // ... rest of init
     }
 
     public BeanRegistry getRegistry() {
-        if (registry == null) {
-            registry = new BeanRegistryImplementation(null);
-            registry.register("router", router);
-        }
-        return registry;
+        return ensureRegistry();
     }
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java (1)

30-33: Use Class<?> instead of raw Class.

Using the raw Class type generates compiler warnings and bypasses generic type safety. Consider parameterizing with a wildcard.

♻️ Suggested fix
     /**
      * The class of the bean.
      */
-    private final Class clazz;
+    private final Class<?> clazz;
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (1)

102-104: Use parameterized logging instead of string concatenation.

String concatenation in log.debug evaluates even when debug logging is disabled, causing unnecessary overhead.

♻️ Suggested fix
         if (bc.produces(BeanCacheObserver.class)) {
             observers.add((BeanCacheObserver) bc.getOrCreate(this, grammar));
-            log.debug("Registered BeanRegistry observer: " + bc);
+            log.debug("Registered BeanRegistry observer: {}", bc);
         }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cec04d and 802b3c0.

📒 Files selected for processing (13)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanCacheObserver.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • annot/src/main/java/com/predic8/membrane/annot/yaml/GenericYamlParser.java
  • annot/src/test/java/com/predic8/membrane/annot/YAMLBeanParsingTest.java
  • annot/src/test/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementationTest.java
  • annot/src/test/java/com/predic8/membrane/annot/util/YamlParser.java
  • core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/BalancerHealthMonitor.java
  • core/src/main/java/com/predic8/membrane/core/kubernetes/KubernetesWatcher.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java
  • core/src/test/java/com/predic8/membrane/core/router/TestRouter.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:51.595Z
Learning: In BeanRegistryImplementation, the `getOrCreate(BeanRegistry, Grammar)` method on BeanContainer is guaranteed to always return a non-null object or throw an exception, so null filtering after calling getOrCreate is not necessary.
📚 Learning: 2026-01-03T19:24:51.595Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:51.595Z
Learning: In BeanRegistryImplementation, the `getOrCreate(BeanRegistry, Grammar)` method on BeanContainer is guaranteed to always return a non-null object or throw an exception, so null filtering after calling getOrCreate is not necessary.

Applied to files:

  • annot/src/test/java/com/predic8/membrane/annot/util/YamlParser.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java
  • annot/src/test/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementationTest.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
  • core/src/main/java/com/predic8/membrane/core/kubernetes/KubernetesWatcher.java
  • annot/src/main/java/com/predic8/membrane/annot/yaml/GenericYamlParser.java
📚 Learning: 2026-01-03T19:24:48.014Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:48.014Z
Learning: In BeanRegistryImplementation.java, the getOrCreate(BeanRegistry, Grammar) method on BeanContainer is guaranteed to return a non-null object or throw an exception. Do not perform or rely on null-filtering after calling getOrCreate; remove any post-call null checks and rely on the contract. If you need extra safety, consider an assertion or explicit exception path for unexpected nulls, but prefer not to branch on null after getOrCreate.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)
🔇 Additional comments (22)
annot/src/test/java/com/predic8/membrane/annot/util/YamlParser.java (1)

50-54: LGTM! Clean refactoring to simplified initialization pattern.

The constructor simplification and explicit start() call align well with the PR's goal of moving observer registration into the registry itself. The initialization sequence is clear and correct.

core/src/main/java/com/predic8/membrane/core/interceptor/balancer/BalancerHealthMonitor.java (3)

53-53: LGTM! Dual initialization pattern for Spring and BeanRegistry.

The class now correctly implements both ApplicationContextAware and BeanRegistryAware, enabling it to work in both Spring-managed and YAML/BeanRegistry-managed contexts.


168-177: LGTM! Registry-based router injection with lifecycle annotations.

The setRegistry method correctly fetches the Router from the registry, and the @PostConstruct annotation ensures init() is called in non-Spring contexts. The orElseThrow() is appropriate here since a missing router is a fatal configuration error.


179-190: LGTM! PreDestroy annotation for proper cleanup.

Adding @PreDestroy ensures the destroy() method is invoked when the bean is managed by the YAML-based registry, matching the DisposableBean behavior in Spring.

annot/src/main/java/com/predic8/membrane/annot/yaml/GenericYamlParser.java (2)

143-156: LGTM! Clean extraction of class resolution logic.

The decideClazz method provides a reusable way to determine the target class for a YAML node without instantiation. This supports the new BeanContainer.produces() capability referenced in other files.


316-318: LGTM! BeanRegistry injection before @PostConstruct.

The registry is correctly injected into BeanRegistryAware beans before invoking @PostConstruct methods, ensuring beans can access the registry during initialization.

core/src/test/java/com/predic8/membrane/core/router/TestRouter.java (2)

44-44: LGTM! Updated to new BeanRegistryImplementation constructor.

The single-argument constructor aligns with the refactored BeanRegistryImplementation that now accepts only a Grammar (or null for test scenarios).


68-72: LGTM! Explicit router registration pattern.

Explicitly registering the router in init() follows the new pattern where the router is registered after registry creation rather than passed via constructor. This aligns with changes in DefaultMainComponents and KubernetesWatcher.

core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java (1)

392-394: LGTM! Improved type-checking with BeanContainer.produces().

The signature change from BeanDefinition to BeanContainer with bc.produces(Proxy.class) is cleaner than the previous grammar-based lookup. This leverages the new type-tracking capability in BeanContainer.

core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (1)

68-74: LGTM! Explicit router registration in init().

The registry is now created with a null grammar and the router is explicitly registered afterward. This follows the new pattern where router injection is decoupled from registry construction.

core/src/main/java/com/predic8/membrane/core/kubernetes/KubernetesWatcher.java (1)

50-52: LGTM! Updated to new BeanRegistryImplementation pattern.

The registry is now constructed with only the Grammar, and the router is explicitly registered afterward. This aligns with the broader refactoring to decouple router injection from registry construction.

core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java (1)

166-177: LGTM! Clean initialization sequence.

The updated flow correctly separates registry construction from dependency registration. The sequence (create registry → register router → parse YAML → finish static config → start registry → start router) ensures beans can reference the router during resolution.

annot/src/test/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementationTest.java (1)

29-34: Test setup correctly mirrors the new API.

The test adapts well to the new constructor signature. Note that the aware mock is registered but not verified in the current tests—consider removing it if it's no longer needed, or adding assertions if it should be invoked.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java (2)

42-54: Constructor changes look correct.

Both constructors properly initialize the clazz field—either via decideClazz for deferred instantiation or via getClass() for pre-existing singletons. This enables the type-aware produces() check.


127-132: LGTM on produces() method.

The assignability check is correctly oriented: clazz.isAssignableFrom(this.clazz) returns true when the bean's type is a subtype of the requested type.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanCacheObserver.java (2)

38-43: Parameter rename improves clarity.

Renaming bean to newBean better distinguishes it from oldBean and aligns with the ADD/MODIFY/DELETE semantics documented in the Javadoc.


49-52: Breaking interface change confirmed: isActivatable now takes BeanContainer.

The sole implementation (DefaultRouter) has been properly updated to use the new signature and correctly leverages BeanContainer.produces(Class) for type-aware filtering of Proxy beans.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (5)

47-47: Synchronized list for observers is appropriate.

Using Collections.synchronizedList allows thread-safe add operations during bean processing. The manual synchronized blocks in isActivatable, handleAsynchronousInitializationResult, and handleBeanEvent are correct for iteration.


73-75: Simplified constructor supports dynamic observer registration.

The single-argument constructor aligns with the new design where observers are discovered and registered dynamically when beans producing BeanCacheObserver are encountered.


99-110: Auto-registration of observers based on bean type.

The produces(BeanCacheObserver.class) check elegantly discovers observers at runtime. However, instantiating the observer via getOrCreate during handleBeanContainerChange means the observer is created before the full bean graph may be available.

Ensure that observer beans don't depend on other beans that haven't been registered yet. If observers need dependencies, consider deferring their instantiation until start().


171-176: Double-checking with produces() and isInstance() is redundant but defensive.

getBeans now filters by produces(clazz) on Line 171, then also checks clazz::isInstance on Line 174. The isInstance check is technically redundant given produces(), but serves as defensive coding against edge cases where getOrCreate might return a different type (e.g., proxies). This is acceptable.


189-205: LGTM on register() method.

The method correctly creates a BeanContainer with the singleton and delegates to handleBeanContainerChange, ensuring consistent handling for both programmatically registered beans and YAML-defined beans.

Comment thread core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java Outdated
@rrayst

rrayst commented Jan 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai please review

@coderabbitai

coderabbitai Bot commented Jan 7, 2026

Copy link
Copy Markdown
Contributor

@rrayst I'll review the changes in this PR now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
@annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:
- Around line 79-82: The start() method currently iterates all BeanContainer
instances and calls bc.getOrCreate(this, grammar) without excluding component
beans, causing component beans to be instantiated at startup contrary to
getBeans() filtering; update start() to skip containers whose
BeanDefinition.isComponent() is true (i.e. only call getOrCreate for
non-component bean definitions) so behavior matches getBeans(), or if
intentional keep a clear comment and document why component beans need eager
init; locate the loop over BeanContainer in start(), check how
BeanDefinition.isComponent() is accessed elsewhere (e.g., getBeans()), and apply
the same filtering before invoking BeanContainer.getOrCreate(this, grammar).
🧹 Nitpick comments (1)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (1)

99-110: Consider error handling for observer registration.

The eager creation of BeanCacheObserver instances at line 103 has no error handling. If observer initialization fails (e.g., due to unmet dependencies or construction errors), it will propagate and potentially break the entire bean registration flow.

Consider wrapping the observer creation in a try-catch to:

  • Log the failure with context
  • Decide whether to fail-fast or continue without that observer
  • Provide better diagnostics for initialization issues
🛡️ Proposed error handling
 private void handleBeanContainerChange(WatchAction action, BeanContainer bc) {
     bcs.put(bc.getDefinition().getUid(), bc);
 
     if (bc.produces(BeanCacheObserver.class)) {
-        observers.add((BeanCacheObserver) bc.getOrCreate(this, grammar));
-        log.debug("Registered BeanRegistry observer: " + bc);
+        try {
+            BeanCacheObserver observer = (BeanCacheObserver) bc.getOrCreate(this, grammar);
+            observers.add(observer);
+            log.debug("Registered BeanRegistry observer: {}", bc);
+        } catch (Exception e) {
+            log.error("Failed to create and register observer for bean {}/{}: {}", 
+                     bc.getDefinition().getNamespace(), bc.getDefinition().getName(), e.getMessage(), e);
+            throw new RuntimeException("Observer registration failed", e);
+        }
     }
 
     if (!bc.getDefinition().isComponent() && isActivatable(bc)) {
         uidsToActivate.add(new UidAction(bc.getDefinition().getUid(), action));
     }
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 802b3c0 and 9fa9828.

📒 Files selected for processing (2)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:51.595Z
Learning: In BeanRegistryImplementation, the `getOrCreate(BeanRegistry, Grammar)` method on BeanContainer is guaranteed to always return a non-null object or throw an exception, so null filtering after calling getOrCreate is not necessary.
📚 Learning: 2026-01-03T19:24:48.014Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:48.014Z
Learning: In BeanRegistryImplementation.java, the getOrCreate(BeanRegistry, Grammar) method on BeanContainer is guaranteed to return a non-null object or throw an exception. Do not perform or rely on null-filtering after calling getOrCreate; remove any post-call null checks and rely on the contract. If you need extra safety, consider an assertion or explicit exception path for unexpected nulls, but prefer not to branch on null after getOrCreate.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
🔇 Additional comments (5)
core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java (1)

391-393: LGTM! Signature aligned with new bean container model.

The method signature and implementation correctly align with the new BeanContainer-based activation model. Using bc.produces(Proxy.class) provides a cleaner, more direct way to check bean compatibility.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (4)

73-75: LGTM! Constructor simplified for runtime observer discovery.

The constructor signature change removes the need for external observer injection, aligning with the PR objective of moving observers into the registry for dynamic discovery.


169-177: LGTM! Cleaner filtering using BeanContainer API.

The change from bd.getDefinition().produces(clazz) to bd.produces(clazz) at line 171 simplifies the filtering logic and properly delegates to the BeanContainer API.


189-205: LGTM! Registration flow aligned with new architecture.

The register method now correctly creates a BeanContainer with a BeanDefinition and delegates to handleBeanContainerChange, maintaining consistency with the unified bean registration flow.


245-277: LGTM! Observer notification methods properly synchronized.

The new helper methods correctly:

  • Synchronize access to the observers list
  • Iterate over all registered observers
  • Delegate lifecycle events appropriately
  • Use the provided parameters (previous issue with unused parameter is resolved)

The addition of IOException to handleBeanEvent is appropriate for bean lifecycle operations that may involve I/O.

Comment thread core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java
@membrane-ci-server

Copy link
Copy Markdown

This pull request needs "/ok-to-test" from an authorized committer.

@predic8 predic8 merged commit 9805b06 into master Jan 7, 2026
4 of 5 checks passed
@predic8 predic8 deleted the registry-aware branch January 7, 2026 12:56
@coderabbitai coderabbitai Bot mentioned this pull request Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants